#pragma once
#include "GameObject.h"
#include <list>
#include "Constants.h"

class Item;
class Potion;
class Resource;
class Tool;
class Character;
class Obstacle;
class Door;
class Equipment;
class Tile;
class RangedWeapon;

class Character : public GameObject {
protected:
	static int levelThresholds[MAX_LEVEL];
	int m_level;
	int m_currentHealth;
	int m_maxHealth;
	int m_stats[NUM_STATS];
	int m_turnCounter;
	Tile* m_tile;
	bool m_humanoid;
	int m_currentWeight;
	int m_regenRate;
	int m_regenCounter;
	int m_armorBonus;
	int m_dodgeBonus;
	int m_block;
	std::map<StatusEffect, int> m_statusEffects;
	GenderType m_gender;
	std::list<Item*>* m_inventory;
	Equipment* m_equipment;
	bool m_hostile;
	int m_experience;
	std::map<Skill, float> m_skills;
	char findOpenLetter();
	void meleeAttack(Character* c, int damage, int accuracy = 0, int speed = 100, bool otherHandEmpty = false, GripType gripType = GT_ONE, MeleeWeaponPrefix prefix = MWP_NONE, MeleeWeaponSuffix suffix = MWS_OF_NONE);
	virtual void callMeleeAttack(Character* c, MeleeType type, Item* item = NULL, bool otherHandEmpty = false);
	bool isOppositeGender(GenderType gender) const;
	virtual void changeStat(Stat stat, int mod);
	virtual bool removeStatusEffect(StatusEffect effect);
	bool addItemToInventory(Item* item);
	virtual void logCombat(Character* enemy, bool nothing = false, bool hit = true, bool blocked = false, int damage = 0, bool crit = false, bool flame = false, bool envy = false, bool lust = false) = 0;
	virtual void logReload(RangedWeapon* weapon, bool noAmmo = false) const = 0;
	virtual bool openDoor(Door* door) = 0;
	virtual void giveExperience(int exp);
	virtual void moveToNewTile(int x, int y);
	Item* findItem(TypeHierarchy typeCategory, int type) const;
public:
	Character(GenericType type, GenderType gender = GT_NONE, bool hostile = false);
	Character(GenericType type, std::ifstream& file);
	virtual ~Character();
	static void insertItem(std::list<Item*>* items, Item* item);
	void setTile(Tile* tile);
	Tile* getTile() const;
	virtual bool pickUp(Item* item);
	std::list<Item*>* getInventory() const;
	virtual void logDetails(TextLog& log) const;
	GenderType getGenderType() const;
	Equipment* getEquipment() const;
	virtual bool move(int x, int y);
	int getTurnCounter() const;
	virtual void modifyTurnCounter(int mod);
	virtual bool equip(EquipmentLocation location, Item* item, bool takeTurn = true);
	virtual bool unequip(EquipmentLocation location);
	bool hasKey(LockType lockType) const;
	void doMeleeAttack(Character* c);
	int getStat(Stat stat) const;
	int getLevel() const;
	HealthStatus getHealthStatus() const;
	virtual bool quaff(Potion* potion);
	virtual bool dealDamage(int damage);
	virtual bool addStatusEffect(StatusEffect effect, int power);
	int hasStatusEffect(StatusEffect effect) const;
	void forceItemInInventory(Item* item);
	void forceRemoveItemFromInventory(Item* item);
	bool inventoryContains(TypeHierarchy typeCategory, int type, int num = 1, bool same = true) const;
	virtual void heal(int amount);
	virtual bool poison(int damage);
	virtual void logTrap(TrapType trap, bool hit = true, int damage = 0) = 0;
	std::string getColoredName() const;
	std::string getColoredBasicName() const;
	bool isHostile() const;
	int getExperience() const;
	int getNextLevelExperience() const;
	virtual std::string getName() const;
	Item* getItem(Item* item) const;
	int getDodgeBonus() const;
	int getArmorBonus() const;
	int getBlock() const;
	bool isHumanoid() const;
	const std::map<Skill, float>& getSkills() const;
	int getSkillLevel(Skill skill) const;
	bool hasSkill(Skill skill) const;
	float getRawSkillLevel(Skill skill) const;
	virtual void trainSkill(Skill skill, float amount);
	virtual void save(std::ofstream& file) const;
	void reload();
};

inline Tile* Character::getTile() const {return m_tile;}
inline std::list<Item*>* Character::getInventory() const {return m_inventory;}
inline GenderType Character::getGenderType() const {return m_gender;}
inline Equipment* Character::getEquipment() const {return m_equipment;}
inline int Character::getTurnCounter() const {return m_turnCounter;}
inline int Character::getStat(Stat stat) const {return m_stats[stat];}
inline int Character::getLevel() const {return m_level;}
inline bool Character::isHostile() const {return m_hostile;}
inline int Character::getExperience() const {return m_experience;}
inline int Character::getNextLevelExperience() const {return m_level == MAX_LEVEL ? 0 : levelThresholds[m_level+1];}
inline int Character::getDodgeBonus() const {return m_dodgeBonus;}
inline int Character::getArmorBonus() const {return m_armorBonus;}
inline int Character::getBlock() const {return m_block;}
inline bool Character::isHumanoid() const {return m_humanoid;}
inline const std::map<Skill, float>& Character::getSkills() const {return m_skills;}
inline int Character::getSkillLevel(Skill skill) const {return int(m_skills.at(skill));}
inline bool Character::hasSkill(Skill skill) const {return m_skills.at(skill) >= 1.0f;}
inline float Character::getRawSkillLevel(Skill skill) const {return m_skills.at(skill);}
